﻿using Sidvall.AuthorizationManager.Data;
using System.Linq;
using System.Threading.Tasks;

namespace Sidvall.AuthorizationManager.UI.Entities
{
    public sealed partial class TreeItemCollection : Sidvall.AuthorizationManager.UI.Entities.Core.BusinessBaseCollection<TreeItem>
    {
        #region Public Members

        #region FindTreeItem

        public TreeItem FindTreeItem(Sidvall.AuthorizationManager.Data.IAuthorizationScope item)
        {
            if (item == null)
                return null;
            if (item.ServerConnectionId == null)
                return null;
            if (item.AuthorizationScopeId == null)
                return null;
            return FindTreeItem(item.ServerConnectionId.Value, item.AuthorizationScopeId.Value);
        }
        public TreeItem FindTreeItem(int serverConnectionId, long applicationId)
        {
            return (from o in this
                    where (o.ServerConnectionId == serverConnectionId) && (o.AuthorizationScopeId == applicationId)
                    select o).FirstOrDefault();
        }

        #endregion
        #region ReloadAsync

        public async Task ReloadAsync()
        {
            System.Collections.Generic.List<System.Threading.Tasks.Task> tasks;
            System.Collections.ObjectModel.Collection<DataFetchResult> dataFetchResults;
            Sidvall.AuthorizationManager.Net.Services.V1.ServerConnection serverConnection;
            TreeItem treeItem;
            DataFetchResult dataFetchResult;
            System.Collections.Generic.HashSet<string> ids;
            int iMax, jMax;

            // Prepare tasks
            dataFetchResults = new System.Collections.ObjectModel.Collection<DataFetchResult>();
            foreach (var serverManager in Sidvall.AuthorizationManager.UI.SystemContext.Current.ServerManagers.ValidItems)
            {
                serverConnection = (from o in Sidvall.AuthorizationManager.SystemContext.Current.UserConfiguration.ServerConnections.Items
                                    where o.ServerConnectionId == serverManager.ServerConnectionId
                                    select o).FirstOrDefault();
                dataFetchResult = new DataFetchResult()
                {
                    ServerManager = serverManager,
                    ServerConnection = serverConnection,
                };
                dataFetchResults.Add(dataFetchResult);
            }

            // Create tasks
            tasks = new System.Collections.Generic.List<System.Threading.Tasks.Task>();
            iMax = dataFetchResults.Count;
            for (int i = 0; i < iMax; i++)
            {
                tasks.Add(ReloadAsync(dataFetchResults, i));
            }
            if (tasks.Count > 0)
                await System.Threading.Tasks.Task.WhenAll(tasks);

            // Reload items
            iMax = dataFetchResults.Count;
            for (int i = 0; i < iMax; i++)
            {
                dataFetchResult = dataFetchResults[i];
                if (dataFetchResult.Exception != null)
                    continue;
                ids = new System.Collections.Generic.HashSet<string>();

                // Servers
                ProcessServer(ids, dataFetchResult.ServerConnection, dataFetchResult.ServerManager);

                // AuthorizationScopes
                foreach (var application in dataFetchResult.AuthorizationScopes)
                    ProcessAuthorizationScope(ids, dataFetchResult.ServerConnection, dataFetchResult.ServerManager, application);

                jMax = Count;
                for (int j = jMax - 1; j >= 0; j--)
                {
                    treeItem = this[j];
                    if (treeItem.ServerConnectionId != dataFetchResult.ServerConnection.ServerConnectionId)
                        continue;
                    if (!ids.Contains(treeItem.TreeItemId))
                    {
                        using (var manager = new Sidvall.Business.BusinessCollectionSettingsManager(this, Sidvall.Business.BusinessCollectionEvents.NotifyCollectionChanged))
                        {
                            Remove(treeItem);
                        }
                    }
                }
            }
        }
        private async Task ReloadAsync(System.Collections.ObjectModel.Collection<DataFetchResult> dataFetchResults, int index)
        {
            DataFetchResult dataFetchResult;
            Sidvall.Data.FetchListCriteria<Data.Fields.AuthorizationScopeField, Data.Relations.AuthorizationScopeRelation> criteria;

            dataFetchResult = dataFetchResults[index];
            try
            {
                criteria = ParameterManager.GetAuthorizationScopeFetchListCriteria(dataFetchResult.ServerConnection.ServerConnectionId.Value);
                dataFetchResult.AuthorizationScopes = await Sidvall.AuthorizationManager.UI.SystemContext.Current.DataPortal.AuthorizationScopeManager.GetItemsAsync(criteria);
                dataFetchResult.Exception = null;
            }
            catch (System.Exception ex)
            {
                dataFetchResult.Exception = ex;
            }
        }

        #endregion

        #endregion
        #region Factory Members

        #region GetTreeItemCollection

        public static TreeItemCollection GetTreeItemCollection()
        {
            TreeItemCollection item;

            item = new TreeItemCollection();

            return item;
        }

        #endregion

        #endregion
        #region Private Members

        private const string FullNameSeparator = "/";

        #region ProcessServer

        private void ProcessServer(System.Collections.Generic.HashSet<string> ids, IServerConnection serverConnection, ServerManager serverManager)
        {
            TreeItem treeItem;
            string key;

            key = TreeItem.GetTreeItemId(serverConnection.ServerConnectionId.Value);
            if (ids.Contains(key))
                return;
            ids.Add(key);
            treeItem = (from o in this
                        where o.TreeItemId == key
                        select o).FirstOrDefault();
            if (treeItem == null)
            {
                treeItem = new TreeItem()
                {
                    ServerConnectionId = serverConnection.ServerConnectionId.Value,
                    IsServer = true,
                    IsAuthorizationScope = false,
                    ServerManager = serverManager,
                };
                Add(treeItem);
            }
            else
            {
                treeItem.ServerManager = serverManager;
            }
            treeItem.SortOrder = TreeItem.GetSortOrder(serverConnection.ServerConnectionId.Value);
            SortItem(treeItem);
            treeItem.Name = serverConnection.Name;
            treeItem.FullName = serverConnection.Name;
            treeItem.ItemTooltip = "Server: " + treeItem.Name;
            treeItem.IsEnabled = serverConnection.IsEnabled;
        }

        #endregion
        #region ProcessAuthorizationScope

        private void ProcessAuthorizationScope(System.Collections.Generic.HashSet<string> ids, IServerConnection serverConnection, ServerManager serverManager, IAuthorizationScope application)
        {
            TreeItem treeItem;
            string key;

            key = TreeItem.GetTreeItemId(serverConnection.ServerConnectionId.Value, application.AuthorizationScopeId);
            if (ids.Contains(key))
                return;
            ids.Add(key);
            treeItem = (from o in this
                        where o.TreeItemId == key
                        select o).FirstOrDefault();
            if (treeItem == null)
            {
                treeItem = new TreeItem()
                {
                    ServerConnectionId = serverConnection.ServerConnectionId.Value,
                    AuthorizationScopeId = application.AuthorizationScopeId,
                    IsServer = false,
                    IsAuthorizationScope = true,
                    ServerManager = serverManager,
                };
                Add(treeItem);
            }
            else
            {
                treeItem.ServerManager = serverManager;
            }
            treeItem.SortOrder = TreeItem.GetSortOrder(serverConnection.ServerConnectionId.Value, application.AuthorizationScopeId);
            SortItem(treeItem);
            treeItem.Name = application.Name;
            treeItem.FullName = serverConnection.Name + FullNameSeparator + treeItem.Name;
            treeItem.ItemTooltip = "Authorization Scope: " + treeItem.Name;
            treeItem.IsEnabled = application.IsEnabled;
        }

        #endregion

        #region SortItem

        private void SortItem(TreeItem item)
        {
            TreeItem nextItem;
            int iMax, index;

            iMax = Count;
            index = IndexOf(item);
            while (index < iMax - 1)
            {
                nextItem = this[index + 1];
                if (item.SortOrder.CompareTo(nextItem.SortOrder) > 0)
                {
                    SwapItems(index, index + 1);
                    index += 1;
                }
                else
                {
                    break;
                }
            }
            while (index > 0)
            {
                nextItem = this[index - 1];
                if (item.SortOrder.CompareTo(nextItem.SortOrder) < 0)
                {
                    SwapItems(index, index - 1);
                    index -= 1;
                }
                else
                {
                    break;
                }
            }
        }

        #endregion
        #region SwapItems

        private void SwapItems(int index1, int index2)
        {
            TreeItem tmpItem;

            tmpItem = this[index1];
            this[index1] = this[index2];
            this[index2] = tmpItem;
        }

        #endregion

        #endregion
        #region Constructors

        private TreeItemCollection()
        {
        }

        #endregion
    }
}